use rand::prelude::random;
use std::time::Duration;
use bevy::{prelude::*, time::common_conditions::on_fixed_timer,  window::PrimaryWindow};
const SNAKE_HEAD_COLOR: Color = Color::rgb(0.2, 0.7, 0.2);
const FOOD_COLOR: Color = Color::rgb(1.0, 0.0, 0.5);
const SNAKE_SEGMENT_COLOR: Color = Color::rgb(0.3, 0.7, 0.7);
const ARENA_WIDTH: u32 = 10;
const ARENA_HEIGHT: u32 = 10;

#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
enum FixedStepSet {
    Tick,
}

#[derive(Component)]
struct SnakeHead{
    direction: Direction,
}

#[derive(Component)]
struct SnakeSegment;

#[derive(Default, Deref, DerefMut,Resource)]
struct SnakeSegments(Vec<Entity>);

#[derive(Component, Clone, Copy, PartialEq, Eq)]
struct Position {
    x: i32,
    y: i32,
}

#[derive(Component)]
struct Size {
    width: f32,
    height: f32,
}
impl Size {            //为定位和设置大小而创建两个结构体，并定义一个方法
    pub fn square(x: f32) -> Self {
        Self {
            width: x,
            height: x,
        }
    }
}
#[derive(PartialEq, Copy, Clone)]
enum Direction {
    Left,
    Up,
    Right,
    Down,
}

impl Direction {
    fn opposite(self) -> Self {
        match self {
            Self::Left => Self::Right,
            Self::Right => Self::Left,
            Self::Up => Self::Down,
            Self::Down => Self::Up,
        }
    }
}

struct GameOverEvent;
struct GrowthEvent;
#[derive(Default, Resource)]
struct LastTailPosition(Option<Position>);

fn setup_camera(mut commands: Commands) {
    commands.spawn(Camera2dBundle ::default());
}


fn spawn_segment(mut commands: Commands, position: Position) -> Entity {  //辅助函数
    commands
        .spawn(SpriteBundle {
            sprite: Sprite {
                color: SNAKE_SEGMENT_COLOR,
                ..default()
            },
            ..default()
        })
        .insert(SnakeSegment)
        .insert(position)
        .insert(Size::square(0.65))
        .id()
}

//生成蛇，蛇头 ，以及之后的蛇身
fn spawn_snake(mut commands: Commands, mut segments: ResMut<SnakeSegments>) {
    *segments = SnakeSegments(vec![
        commands
            .spawn(SpriteBundle {
                sprite: Sprite {
                    color: SNAKE_HEAD_COLOR,
                    ..default()
                },
                ..default()
            })
            .insert(SnakeHead {
                direction: Direction::Up,
            })
            .insert(SnakeSegment)
            .insert(Position { x: 3, y: 3 })
            .insert(Size::square(0.8))
            .id(),
        spawn_segment(commands, Position { x: 3, y: 2 }),
    ]);
}

fn snake_movement(
    mut last_tail_position: ResMut<LastTailPosition>,
    mut game_over_writer: EventWriter<GameOverEvent>,
    segments: ResMut<SnakeSegments>,
    mut heads: Query<(Entity, &SnakeHead)>,
    mut positions: Query<&mut Position>,
) {
    if let Some((head_entity, head)) = heads.iter_mut().next() {
        let segment_positions = segments
            .iter()
            .map(|e| *positions.get_mut(*e).unwrap())
            .collect::<Vec<Position>>();
        let mut head_pos = positions.get_mut(head_entity).unwrap();
        match &head.direction {
            Direction::Left => {
                head_pos.x -= 1;
            }
            Direction::Right => {
                head_pos.x += 1;
            }
            Direction::Up => {
                head_pos.y += 1;
            }
            Direction::Down => {
                head_pos.y -= 1;
            }
        };
        if head_pos.x < 0
        || head_pos.y < 0
        || head_pos.x as u32 >= ARENA_WIDTH
        || head_pos.y as u32 >= ARENA_HEIGHT
    {
        game_over_writer.send(GameOverEvent);
    }
    if segment_positions.contains(&head_pos) {
        game_over_writer.send(GameOverEvent);
    }
        segment_positions      //更新蛇身的方式是之后的小块都走到前面的小块的位置以达到整体移动的效果
            .iter()
            .zip(segments.iter().skip(1))
            .for_each(|(pos, segment)| {
                *positions.get_mut(*segment).unwrap() = *pos;
            });
            *last_tail_position = LastTailPosition(Some(*segment_positions.last().unwrap()));
    }
}

fn snake_movement_input(keyboard_input: Res<Input<KeyCode>>, mut heads: Query<&mut SnakeHead>) {
    if let Some(mut head) = heads.iter_mut().next() {
        let dir: Direction = if keyboard_input.pressed(KeyCode::Left) {
            Direction::Left
        } else if keyboard_input.pressed(KeyCode::Down) {
            Direction::Down
        } else if keyboard_input.pressed(KeyCode::Up) {
            Direction::Up
        } else if keyboard_input.pressed(KeyCode::Right) {
            Direction::Right
        } else {
            head.direction
        };
        if dir != head.direction.opposite() {
            head.direction = dir;
        }
    }
}
fn game_over(
    mut commands: Commands,
    mut reader: EventReader<GameOverEvent>,
    segments_res: ResMut<SnakeSegments>,
    food: Query<Entity, With<Food>>,
    segments: Query<Entity, With<SnakeSegment>>,
) {
    if reader.iter().next().is_some() {
        for ent in food.iter().chain(segments.iter()) {
            commands.entity(ent).despawn();
        }
        spawn_snake(commands, segments_res);
    }
}


fn snake_eating(
    mut commands: Commands,
    mut growth_writer: EventWriter<GrowthEvent>,
    food_positions: Query<(Entity, &Position), With<Food>>,
    head_positions: Query<&Position, With<SnakeHead>>,
) {
    for head_pos in head_positions.iter() {
        for (ent, food_pos) in food_positions.iter() {
            if food_pos == head_pos {
                commands.entity(ent).despawn();
                growth_writer.send(GrowthEvent);
            }
        }
    }
}

fn snake_growth(
    commands: Commands,
    last_tail_position: Res<LastTailPosition>,
    mut segments: ResMut<SnakeSegments>,
    mut growth_reader: EventReader<GrowthEvent>,
) {
    if growth_reader.iter().next().is_some() {
        segments.push(spawn_segment(commands, last_tail_position.0.unwrap()));
    }
}



fn size_scaling(primary_query: Query<&Window, With<PrimaryWindow>>, mut q: Query<(&Size, &mut Transform)>) {
    let Ok(window) = primary_query.get_single() else {//single函数确保只取一个Windows实体
        return;
    };
    for (sprite_size, mut transform) in q.iter_mut() {   //遍历每个实体将每个sprite的长宽适当放缩并存入transform
        transform.scale = Vec3::new(                                            //也就是根据窗口大小动态调整实体
            sprite_size.width / ARENA_WIDTH as f32 * window.width() as f32,
            sprite_size.height / ARENA_HEIGHT as f32 * window.height() as f32,
            1.0,
        );
    }
}
//将游戏世界中的位置转化为窗口的实际渲染位置
fn position_translation( primary_query: Query<&Window, With<PrimaryWindow>>, mut q: Query<(&Position, &mut Transform)>) {
    fn convert(pos: f32, bound_window: f32, bound_game: f32) -> f32 {
        let tile_size = bound_window / bound_game;
        pos / bound_game * bound_window - (bound_window / 2.) + (tile_size / 2.)
    }
    let Ok(window) = primary_query.get_single() else {
        return;
    };
    for (pos, mut transform) in q.iter_mut() {
        transform.translation = Vec3::new(
            convert(pos.x as f32, window.width() as f32, ARENA_WIDTH as f32),
            convert(pos.y as f32, window.height() as f32, ARENA_HEIGHT as f32),
            0.0,
        );
    }
}

//调整窗口的标题，形状大小
fn window_size(mut primary_query: Query<&mut Window, With<PrimaryWindow>>) {    //mut windows: ResMut<Window>  
    let mut window = primary_query.get_single_mut().unwrap(); //在0.10中window资源不能再这样调用，用查询的方式使用

    window.title="驯蛇大师毛梓聿".to_string();
    window.resolution=(1300., 1300.).into();
}

//制作随机的食物
#[derive(Component)]
struct Food ;
fn food_spawner(mut commands: Commands) {        //command类用于创建实体
    commands
        .spawn(SpriteBundle {
            sprite: Sprite {
                color: FOOD_COLOR,
                ..default()
            },
            ..default()
        })
        .insert(Food)
        .insert(Position {
            x: (random::<f32>() * ARENA_WIDTH as f32) as i32,
            y: (random::<f32>() * ARENA_HEIGHT as f32) as i32,
        })
        .insert(Size::square(0.8));
}


fn main() {
    App::new()
    .add_plugins(DefaultPlugins)
    .insert_resource(ClearColor(Color::rgb(0.04, 0.04, 0.04)))
    .insert_resource(SnakeSegments::default())
    .insert_resource(LastTailPosition::default())
    .add_event::<GrowthEvent>()
    .add_event::<GameOverEvent>()
    .add_startup_systems((window_size,setup_camera,spawn_snake))//启动系统可以一起写
    .add_system(snake_movement_input.before(snake_movement))
    .add_system(snake_movement_input.in_schedule(CoreSchedule::FixedUpdate))
    .edit_schedule(CoreSchedule::FixedUpdate, |schedule| {
        schedule
            .add_systems(
                (snake_movement, snake_eating)
                    .chain()
                    .in_set(FixedStepSet::Tick),
            )
            .configure_set(
                FixedStepSet::Tick.run_if(on_fixed_timer(Duration::from_secs_f32(0.25))),
            );
    })
    .add_system(snake_growth.run_if(on_event::<GrowthEvent>()))
    .add_system(game_over.after(snake_movement))
    .add_systems((position_translation, size_scaling).in_base_set(CoreSet::PostUpdate))
    .add_system(
        food_spawner
            .in_schedule(CoreSchedule::FixedUpdate)//表明系统的调度策略为固定更新阶段实行
            .run_if(on_fixed_timer(Duration::from_secs(1))),//run_if是系统运行条件，规定每一秒运行一次
    )
    .run();
    
}